#include <math.h>
#include <stdio.h>
#define U_INT16 unsigned int

int display_as_real;
int display_debug;

#define TRUE  1
#define FALSE 0

/*                                                               */
/*        MASS FLOW SENSOR  ADC-Flow, Flow-ADC                   */
/*                                                               */
/*        conversion table generator.                            */
/*                                                               */
/*                                          ( beta * flow )      */
/*   Sensor    =    gamma + alpha * (  1 - e               )     */
/*   Voltage                                                     */
/*                                                               */
/*     gamma = sensor voltage with flow = 0                      */
/*     alpha + gamma = maximum sensor voltage (infinite flow)    */ 
/*     beta  = shape of curve,  decay rate                       */
/*                                                               */

#define IU_SCALING 16384

typedef struct
        {
             char gas[20];
             char sensor[20];
             float alpha;
             float beta;
             float gamma;
             float max_flow;
        
        } fs_entry;

#define NUM_ENTRIES 5

fs_entry  fs_table[NUM_ENTRIES] = {
         { "N2",  "LOW",    2.3,        -0.011200,   0.795,     200.0 },

         { "He",  "LOW",    4.16,       -0.001025,     1.0,    1100.0 },

         { "H2",  "LOW",    4.37,       -0.001148,     1.0,    1100.0 },

         { "Ar",  "LOW",    4.410,      -0.005615,     1.0,     200.0 },

         { "ArMe","LOW",    4.410,      -0.005615,     1.0,    200.0 } };





unsigned int flow_to_digital ( float flow, float alpha, float beta, float gamma )
{
    float sensor;
    unsigned int digital;

    flow = flow*(273.15/298.15); /* mL/Min@NTP -> SCCM */ 

    sensor = gamma + alpha*(1-exp(beta*flow));

    digital = sensor*16384;

    if (display_debug)  printf ( "Flow: %4.2f  ", flow );
    if (display_debug)  printf ( "Sensor:  %4.2f  ", sensor );
    if (display_debug)   printf ( "digital: %6x\n", digital);

    return digital;
}

float digital_to_flow ( unsigned int digital, float alpha, float beta, float gamma )
{

    float sensor;
    float flow;

    sensor = (float)digital/16384.0 ;

      /* check for sensor values larger than possible */
    if ((gamma-sensor)/alpha < -1) sensor = alpha + gamma - 1e-6;

    flow = log(1-((sensor - gamma)/alpha))/beta ;

    flow = flow/(273.15/298.15); /* SCCM -> mL/Min@NTP */

      /* flows can never be negative */
    if (flow < 0) flow = 0;

    if (display_debug)  printf ( "digital: %6x  ", digital );
    if (display_debug)   printf ( "Sensor: %4.2f ", sensor );
    if (display_debug)  printf ( "Flow: %4.2f\n",   flow );

    return flow;
}
   


      

void print_digital_to_flows ( char *gas,
                            float max,
                            float alpha,
                            float beta,
                            float gamma )
{

    unsigned int i;
    unsigned int digital;
    float flow;

    
    printf ( "\f\nconst U_INT32  %s_ADC_To_Flow[] = {          \n", gas );
   
    for (i= 0 ; i<256 ; i++) 
    {
        digital =  i*256;
        flow    = digital_to_flow(digital ,alpha,beta,gamma );

        /*if ( flow*1.1 > max ) break;*/

        if (display_debug != TRUE )
        {
            if ((i%6)==0)
            {
               if (display_as_real)
                   printf ( "\n/*%6.2f */  %8.2f,", (float)digital/10922.0, flow );
               else
                   printf ( "\n/*%5x */  0x%-8lx,", digital, (unsigned long)(flow*IU_SCALING) );
            }
            else
            {
               if (display_as_real)
                   printf ( " %8.2f,", flow );
               else
    	           printf ( " 0x%-8lx,",(unsigned long)(flow*IU_SCALING) );
            }
       }
    }

    digital = i*256-1;
    flow    = digital_to_flow(digital ,alpha,beta,gamma );

    if (display_debug != TRUE )
    {
        if (display_as_real)
            printf ( " %8.2f };\n", flow );
        else
            printf ( " 0x%-8lx };\n",(unsigned long)(flow*IU_SCALING) );
    }

}


void print_flows_to_digital( char *gas,
                            float max,
                            float alpha,
                            float beta,
                            float gamma )
{

    float step_rate;
    int i;
    float flow;
    unsigned int digital;

    if      ( max <= 256 ) step_rate = 1;
    else if ( max <= 512 ) step_rate = 2;
    else if ( max <= 1024) step_rate = 4;
    else if ( max <= 2048) step_rate = 8;
    else if ( max <= 4096) step_rate = 16;

    printf ( "\f\nconst U_INT16 %s_Flow_To_ADC[] = {\n", gas );

    for (i= 0 ; i<255 ; i++ )
    {
        flow = i*step_rate;
   
        digital = flow_to_digital(flow ,alpha,beta,gamma );

        if (display_debug != TRUE )
        {
            if ((i%8)==0)
            {
               if (display_as_real)
	          printf ( "\n/*%7.2f */  %6.2f,", flow, (float)digital/10922.0 );
               else
	          printf ( "\n/*%7.2f */  0x%-6x,", flow, digital );
            }
            else
            {
               if (display_as_real)
	          printf ( " %6.2f,", (float)digital/10922.0 );
               else
	          printf ( " 0x%-6x,", digital );
            }
       }
    }

    flow = i*step_rate;
    digital = flow_to_digital(flow ,alpha,beta,gamma );

    if (display_debug != TRUE )
    {
        if (display_as_real)
            printf ( " %6.2f };\n", (float)digital/10922.0 );
        else
            printf ( " 0x%-6x };\n", digital );
    }


}


void print_flow_per_bit ( char *gas,
                          float alpha,
                          float beta,
                          float gamma )
{
     int zero_flow_digital;

     zero_flow_digital = flow_to_digital( 0.0, alpha, beta, gamma );

     printf ( "\n\n#define %s_FLOW_PER_BIT  0x%lx\n\n", gas,
                  (unsigned long)(digital_to_flow( zero_flow_digital +1, alpha, beta, gamma )*IU_SCALING) );

}

void print_tblu_index_mask ( char *gas, float max )
{

   unsigned long mask;
   int shift_value;

   if      ( max <= 256 ) {  mask = 0x003fffc0 ; shift_value = 6; }
   else if ( max <= 512 ) {  mask = 0x007fff80 ; shift_value = 7; }
   else if ( max <= 1024) {  mask = 0x00ffff00 ; shift_value = 8; }
   else if ( max <= 2948) {  mask = 0x01fffe00 ; shift_value = 9; }
   else if ( max <= 4096) {  mask = 0x03fffc00 ; shift_value =10; }
   else if ( max <= 8192) {  mask = 0x07fff800 ; shift_value =11; }

   
   printf ( "\n#define %s_TBLU_MASK   0x%x\n\n", gas, mask );
   printf ( "\n#define %s_TBLU_SHIFT  %d\n\n", gas, shift_value );

}


print_max_flow (  char *name, float alpha, float beta, float gamma )
{
    unsigned int digital;
    float flow;

    digital = 127*256;
    flow    = digital_to_flow(digital ,alpha,beta,gamma );

    if (display_debug != TRUE )
    {
        if (display_as_real)
            printf ( " %8.2f };\n", flow );
        else
            printf ( "#define %s_MAX_FLOW   0x%-8lx \n\n\n", name, (unsigned long)(flow*IU_SCALING) );
    }


}

print_second_order_stuff (  char *name, float alpha, float beta, float gamma )
{

    float    alpha_beta;

    alpha_beta = -beta * alpha * 16384;


    if (display_debug != TRUE )
    {
        if (display_as_real)
            printf ( " %8.2f };\n", alpha_beta );
        else
            printf ( "\n\n\n#define %s_alpha_beta         0x%-8lx \n", name, (unsigned long)(alpha_beta) );
            printf ( "#define %s_alpha_beta_scale   0x%-8lx \n\n\n", name, (unsigned long)(16384) );
    }


}


int main(int argc, char *argv[] )
{
   char  name[40];
   int   index;

   display_as_real = FALSE;
   display_debug   = FALSE;


   if ( argc >= 1 ) 
   {
      if ( strcmp( "-r", argv[1] ) == 0 )  display_as_real = TRUE;
      if ( strcmp( "-d", argv[1] ) == 0 )  display_debug   = TRUE;
   }


   for ( index=0 ; index < NUM_ENTRIES ; index++ )
   {
  

       strcpy( name, fs_table[index].gas );
       strcat( name, "_" );
       strcat( name, fs_table[index].sensor );

       print_digital_to_flows ( name,
                              flow_to_digital(fs_table[index].max_flow,
                                              fs_table[index].alpha,
                                              fs_table[index].beta,
                                              fs_table[index].gamma ),
                              fs_table[index].alpha,
                              fs_table[index].beta,
                              fs_table[index].gamma );
    
       print_flows_to_digital ( name,
                              fs_table[index].max_flow,
                              fs_table[index].alpha,
                              fs_table[index].beta,
                              fs_table[index].gamma );

       print_second_order_stuff ( name,
                              fs_table[index].alpha,
                              fs_table[index].beta,
                              fs_table[index].gamma );
                               
    
       print_flow_per_bit     ( name,
                              fs_table[index].alpha,
                              fs_table[index].beta,
                              fs_table[index].gamma );
    
       print_tblu_index_mask  ( name, fs_table[index].max_flow );

       print_max_flow ( name, fs_table[index].alpha,
                              fs_table[index].beta,
                              fs_table[index].gamma );

    }

    return 0;

}

